package com.novunda.platform.services;

import java.util.Date;
import java.util.List;

import com.novunda.platform.common.handler.LogCallBack;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.novunda.platform.common.context.SpringContextHolder;
import com.novunda.platform.common.exception.NovException;
import com.novunda.platform.common.reponse.CommonResponse;
import com.novunda.platform.common.utils.PWDUtils;
import com.novunda.platform.common.utils.UserUtils;
import com.novunda.platform.common.web.modal.Page;
import com.novunda.platform.entities.Log;
import com.novunda.platform.entities.Menu;
import com.novunda.platform.entities.Role;
import com.novunda.platform.entities.User;
import com.novunda.platform.entities.base.BaseEntity;
import com.novunda.platform.repositories.CenterRepository;
import com.novunda.platform.repositories.MenuRepository;
import com.novunda.platform.repositories.RoleRepository;
import com.novunda.platform.repositories.UserRepository;
import com.novunda.platform.security.CommonAuthorizingRealm;
import com.novunda.tca.tdk.domain.DeviceInfo;
import com.novunda.tca.tdk.domain.DeviceInfoResponse;

/**
 * @author linfeng
 * @since 2016/9/5
 */
@Service
@Transactional
public class PlatformService extends BaseService {

    public static final String HASH_ALGORITHM = "SHA-1";
    public static final int HASH_INTERATIONS = 1024;
    public static final int SALT_SIZE = 8;

    @Autowired
    private UserRepository userRepository;
    @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private MenuRepository menuRepository;
    @Autowired
    private CenterRepository centerRepository;

    private List<CommonAuthorizingRealm> systemRealm;

    // -- User Service --//

    public User getUser(Long id) {
        return userRepository.findOne(id);
    }

    public Page<User> findUser(Page<User> page, User user) {
        userRepository.clear();
        DetachedCriteria dc = userRepository.createDetachedCriteria();
        User currentUser = UserUtils.getUser();
        dc.createAlias("company", "company");
        dc.createAlias("office", "office");
        dc.add(dataScopeFilter(currentUser, "office", ""));
        String keyword = user.getRealName();
        if (StringUtils.isNotEmpty(keyword)) {
        	keyword = keyword.trim();
        	dc.add(Restrictions.or(Restrictions.like("realName", "%" + keyword + "%"),
                    Restrictions.like("loginName", "%" + keyword + "%"),
                    Restrictions.like("company.name", "%" + keyword + "%"),
                    Restrictions.like("office.name", "%" + keyword + "%")));
        }
        // 排除admin
        dc.add(Restrictions.not(Restrictions.eq("id", 1l)));
        dc.add(Restrictions.eq(BaseEntity.DEL_FLAG, BaseEntity.DEL_FLAG_NORMAL));
        dc.addOrder(Order.desc("createDate"));
        return userRepository.find(page, dc);
    }

    public User getUserByLoginName(String loginName) {
        userRepository.clear();
        return userRepository.findByLoginName(loginName);
    }

    @Transactional(readOnly = false)
    public void saveUser(final User user) {
        handler.doHandler(new LogCallBack() {
            @Override
            public Object doHandler() {
                userRepository.clear();
                userRepository.save(user);
                clearAllCachedAuthorizationInfo();
                return null;
            }

            @Override
            public Log generate() {
                Log log = new Log();
                String operation = user.getId() == null ? "添加" : "修改";
                log.setType(operation);
                log.setName(operation + "账号");
                log.setDescription(operation + "账号：" + user.getLoginName());
                return log;
            }

            @Override
            public boolean forceLog() {
                return false;
            }
        });

    }

    @Transactional(readOnly = false)
    public void deleteUser(final Long id) throws NovException {
    	final User user = userRepository.getOne(id);
        final String userName = user.getLoginName();
        handler.doHandler(new LogCallBack() {
            @Override
            public Object doHandler() {
                // 删除账号先解除该账号下所有绑定的设备
                DeviceInfoResponse deviceResponse = centerRepository.devices(userName, "-1");
                List<DeviceInfo> devices = deviceResponse.getInfoList();
                // 如果有绑定设备，先解绑再删除账号
                if (null != devices && !devices.isEmpty()) {
                    for (DeviceInfo device : devices) {
                        CommonResponse commonResponse = centerRepository.release(user.getLoginName(), device.getDeviceInstallCode());
                        if (commonResponse == null || commonResponse.getReturnCode() != 0) {
                            throw new NovException("解除绑定失败");
                        }
                    }
                }
                // 删除账号
                userRepository.deleteById(id);
                return null;
            }

            @Override
            public Log generate() {
                Log log = new Log();
                log.setType("删除");
                log.setName("删除账号");
                log.setDescription("删除账号：" + user.getLoginName());
                return log;
            }

            @Override
            public boolean forceLog() {
                return false;
            }
        });

    }

    @Transactional(readOnly = false)
    public void updatePasswordById(Long id, String loginName, String newPassword) {
        userRepository.clear();
        userRepository.updatePasswordById(PWDUtils.entryptPassword(newPassword), id);
        clearCachedAuthorizationInfo(loginName);
    }

    @Transactional(readOnly = false)
    public void updateUserLoginInfo(Long id) {
        userRepository.clear();
        userRepository.updateLoginInfo(SecurityUtils.getSubject().getSession().getHost(), new Date(),
                id);
    }

    // -- Role Service --//

    public Role getRole(Long id) {
        return roleRepository.findOne(id);
    }

    public Role findRoleByNameAndOffice(String name, long officeId) {
        return roleRepository.findByNameAndOffice(name, officeId);
    }

    public List<Role> findAllRole() {
        roleRepository.clear();
        User user = UserUtils.getUser();
        DetachedCriteria dc = roleRepository.createDetachedCriteria();
        dc.createAlias("office", "office");
        dc.createAlias("userList", "userList", JoinType.LEFT_OUTER_JOIN);
        dc.add(dataScopeFilter(user, "office", "userList"));
        dc.add(Restrictions.eq(BaseEntity.DEL_FLAG, BaseEntity.DEL_FLAG_NORMAL));
        dc.addOrder(Order.desc("createDate"));
        return roleRepository.find(dc);
    }

    public List<User> findUsersByRoleId(Long roleId) {
        DetachedCriteria dc = userRepository.createDetachedCriteria();
        dc.createAlias("roleList", "roleList", JoinType.LEFT_OUTER_JOIN);
        dc.add(Restrictions.eq("roleList.id", roleId));
        dc.add(Restrictions.eq(BaseEntity.DEL_FLAG, BaseEntity.DEL_FLAG_NORMAL));
        return userRepository.find(dc);
    }

    /**
     * .
     * 功能说明：保存一次
     *
     * @param role 角色实体
     */
    @Transactional(readOnly = false)
    public void saveOnce(final Role role) {
        handler.doHandler(new LogCallBack() {
            @Override
            public Object doHandler() {
                roleRepository.save(role);
                return null;
            }

            @Override
            public Log generate() {
                Log log = new Log();
                String operation = role.getId() == null ? "添加" : "修改";
                log.setType(operation);
                log.setName(operation + "角色");
                log.setDescription(operation + "角色" + role.getName());
                return log;
            }

            @Override
            public boolean forceLog() {
                return false;
            }
        });

    }

    @Transactional(readOnly = false)
    public void saveRole(final Role role) {

        handler.doHandler(new LogCallBack() {
            @Override
            public Object doHandler() {
//                roleRepository.clear();
                roleRepository.save(role);

                UserUtils.removeCache(UserUtils.CACHE_OFFICE_LIST);

                clearAllCachedAuthorizationInfo();
                return null;
            }

            @Override
            public Log generate() {
                Log log = new Log();
                String operation = role.getId() == null ? "添加" : "修改";
                log.setType(operation);
                log.setName(operation + "角色");
                log.setDescription(operation + "角色：" + role.getName());
                return log;
            }

            @Override
            public boolean forceLog() {
                return false;
            }
        });
    }

    @Transactional(readOnly = false)
    public void deleteRole(final Long id) {
        handler.doHandler(new LogCallBack() {
            @Override
            public Object doHandler() {
                roleRepository.clear();
                roleRepository.deleteById(id);
                clearAllCachedAuthorizationInfo();
                return null;
            }

            @Override
            public Log generate() {
                Log log = new Log();
                log.setType("删除");
                log.setName("删除角色");
                log.setDescription("删除角色id：" + id);
                return log;
            }

            @Override
            public boolean forceLog() {
                return false;
            }
        });

    }

    @Transactional(readOnly = false)
    public Boolean outUserInRole(Role role, Long userId) {
        User user = userRepository.findOne(userId);
        List<Long> roleIds = user.getRoleIdList();
        List<Role> roles = user.getRoleList();
        //
        if (roleIds.contains(role.getId())) {
            roles.remove(role);
            saveUser(user);
            return true;
        }
        return false;
    }

    @Transactional(readOnly = false)
    public User assignUserToRole(Role role, Long userId) {
        User user = userRepository.findOne(userId);
        List<Long> roleIds = user.getRoleIdList();
        if (roleIds.contains(role.getId())) {
            return null;
        }
        user.getRoleList().add(role);
        saveUser(user);
        return user;
    }

    // -- Menu Service --//

    public Menu getMenu(Long id) {
        return menuRepository.findOne(id);
    }

    public List<Menu> findAllMenu() {
        return UserUtils.getMenuList();
    }

    @Transactional(readOnly = false)
    public void saveMenu(final Menu menu1) {

        handler.doHandler(new LogCallBack() {
            Menu menu = menu1;

            @Override
            public Object doHandler() {
                menu.setParent(getMenu(menu.getParent().getId()));
                String oldParentIds = menu.getParentIds(); // 获取修改前的parentIds，用于更新子节点的parentIds
                menu.setParentIds(menu.getParent().getParentIds() + menu.getParent().getId() + ",");
                menuRepository.clear();
                menuRepository.save(menu);
                // 更新子节点 parentIds
                List<Menu> list = menuRepository.findByParentIdsLike("%," + menu.getId() + ",%");
                for (Menu e : list) {
                    e.setParentIds(e.getParentIds().replace(oldParentIds, menu.getParentIds()));
                }
                menuRepository.save(list);
                clearAllCachedAuthorizationInfo();
                // 清空菜单缓存
                cleanMenuCache();
                return null;
            }

            @Override
            public Log generate() {
                Log log = new Log();
                log.setType("编辑菜单");
                log.setName("编辑菜单");
                log.setDescription("编辑菜单" + (menu == null ? "" : menu.getName()));
                return log;
            }

            @Override
            public boolean forceLog() {
                return false;
            }

        });

    }

    @Transactional(readOnly = false)
    public void deleteMenu(final Long id) {

        handler.doHandler(new LogCallBack() {
            @Override
            public Object doHandler() {

                menuRepository.clear();
                menuRepository.deleteById(id, "%," + id + ",%");
                clearAllCachedAuthorizationInfo();
                UserUtils.removeCache(UserUtils.CACHE_MENU_LIST);
                UserUtils.removeCache(UserUtils.CACHE_HORIZANTAL_MENU_LIST);
                return null;

            }

            @Override
            public Log generate() {
                Menu menu = menuRepository.findOne(id);
                Log log = new Log();
                log.setType("删除菜单");
                log.setName("删除菜单");
                log.setDescription("删除菜单" + (menu == null ? "" : menu.getName()));
                return log;
            }

            @Override
            public boolean forceLog() {
                return false;
            }

        });

    }

    private void cleanMenuCache() {
        UserUtils.removeCache(UserUtils.CACHE_MENU_LIST);
        UserUtils.removeCache(UserUtils.CACHE_HORIZANTAL_MENU_LIST);
        UserUtils.removeCache(UserUtils.CACHE_OFFICE_LIST);
    }

    private List<CommonAuthorizingRealm> getAuthorizingRealm() {
        if (systemRealm == null) {
            systemRealm = Lists.newArrayList();
            DefaultWebSecurityManager securityManager = SpringContextHolder.getBean("securityManager");
            for (Realm realm : securityManager.getRealms()) {
                systemRealm.add((CommonAuthorizingRealm) realm);
            }
        }
        return systemRealm;
    }

    private void clearAllCachedAuthorizationInfo() {
        for (CommonAuthorizingRealm realm : getAuthorizingRealm()) {
            realm.clearAllCachedAuthorizationInfo();
        }
    }

    private void clearCachedAuthorizationInfo(String principal) {
        for (CommonAuthorizingRealm realm : getAuthorizingRealm()) {
            realm.clearCachedAuthorizationInfo(principal);
        }
    }

    /**
     * 登录日志记录
     */
    public void loginLog() {

        handler.doHandler(new LogCallBack() {
            @Override
            public Object doHandler() {
                return null;
            }

            @Override
            public Log generate() {
                Log log = new Log();
                log.setType("登录");
                log.setName("登录系统");
                log.setDescription("登录系统");
                return log;
            }

            @Override
            public boolean forceLog() {
                return false;
            }
        });
    }

}
